<?php

declare(strict_types=1);

namespace App\Controller\Mini\V2\Payment;

use App\Constants\Order\OrderCode;
use App\Constants\Payment\PaymentCode;
use App\Controller\Mini\V2\Controllers;
use App\Exception\InternalException;
use App\Factory\Payment\Factory;
use App\Model\Orders\Order;
use App\Model\Users\Payer;
use App\Services\Orders\Order as OrderServices;
use Hyperf\Collection\Arr;
use Psr\Http\Message\ResponseInterface;
use App\Request\Payment\Payment as PaymentRequest;
use App\Services\Payment\Payment as PaymentServices;
use App\Services\Users\Payer as PayerServices;
use function Hyperf\Translation\__;

class Payment extends Controllers
{
    /**
     * 请求类
     * @var string
     */
    protected string $requestClass = PaymentRequest::class;

    public function __construct(
        PaymentServices $paymentServices,
        protected OrderServices $orderServices,
        protected PayerServices $payerServices
    )
    {
        parent::__construct();
        $this->services = $paymentServices;
    }

    /**
     * 支付下单
     * @param Factory $factory
     * @return ResponseInterface
     * @throws \Throwable
     */
    public function save(Factory $factory):ResponseInterface
    {
        $input = $this->input(['no', 'payer_id']);

        $payCode = Arr::get($input, 'code', 'safepayMini');

        /** @var Order $orderModel */
        $orderModel = $this->orderServices->search($input)->firstOrFail();
        if ($orderModel->status == OrderCode::STATUS_CLOSE) {
            return failed(__('message.order.close'));
        }
        if (!in_array($orderModel->pay_status, [OrderCode::PAY_STATUS_PENDING_PAID, OrderCode::PAY_STATUS_PART_PAID])) {
            return failed(__('message.order.status.failed'));
        }

        /** @var Payer $payerModel */
        $payerModel = $this->payerServices->findOrFail($input['payer_id']);

        // 如果订单没有绑定认证信息则去绑定认证信息
        if (!$orderModel->payer ||
            $payerModel->id_card !== $orderModel->payer_no ||
            $payerModel->mobile !== $orderModel->payer_mobile) {
            $orderModel->fill([
                'payer_name' => $payerModel->name,
                'payer_no' => $payerModel->id_card,
                'payer_mobile' => $payerModel->mobile,
                'payer' => $payerModel
            ])->save();
        }

        /** @var \App\Model\Payment\Payment $model */
        if ($orderModel->paid_no && $model = $this->services
                ->where('no', $orderModel->paid_no)
                ->where('type', PaymentCode::TYPE_PAYMENT)
                ->first()) {
            if ($model->notify_body && $model->process_at) {
                return failed(__('message.order.paid'));
            }
            if (!str_contains($model->code, 'safepay') || (str_contains($model->code, 'safepay') && $model->created_at->gt(date('Y-m-d H:i:s', time()-60)))) {
                if (!isset($model->response_body['code']) ||
                    $model->response_body['code'] !== '0' ||
                    !isset($model->response_body['data']) ||
                    !isset($model->response_body['data']['code']) ||
                    $model->response_body['data']['code'] != '00') {
                    throw new InternalException($model->response_body['message']);
                }
                return success($model->response_body['data']['resultContent']);
            }
        }

        $param = [
            'out_trade_no' => $orderModel->no,
            'payMethon' => 'NeoPay',
            'amount' => $orderModel->paid_amount,
            'description' => "跨境订单支付:{$orderModel->no}",
            'storeName' => $orderModel->shop->name,
            'openid' => getInfo('mini', 'openid')
        ];

        [$request, $response, $responseParams] = $factory->{$payCode}($param);

        $this->services->create([
            'order_no' => $orderModel->no,
            'amount' => $param['amount'],
            'no' => $response['data']['trace'],
            'user_id' => $orderModel->user_id,
            'request_body' => $request,
            'response_body' => $response,
            'type' => PaymentCode::TYPE_PAYMENT,
            'code' => $payCode
        ]);
        $orderModel->fill(['paid_no' => $response['data']['trace']])->save();

        return success($responseParams);
    }
}
